package org.example.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author xietl
 * @date 2025/10/5  16:21
 * @description redis分布式锁的配置中心
 */
@Component
public class RedisLockUtilConfig {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    private static final long DEFAULT_EXPIRE = 30000; // 锁默认过期时间30秒
    private static final long DEFAULT_RETRY = 1000;   // 重试间隔1秒
    private static final int DEFAULT_RETRY_TIMES = 3; // 最大重试3次

    // 获取分布式锁
    public boolean tryLock(String lockKey, long expireMs, long retryMs, int retryTimes) {
        String lockValue = UUID.randomUUID().toString();
        int retryCount = 0;
        while (retryCount < retryTimes) {
            // SET NX EX：原子操作，确保加锁唯一性
            Boolean success = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, expireMs, TimeUnit.MILLISECONDS);
            if (Boolean.TRUE.equals(success)) {
                // 加锁成功，存储value用于释放锁（防止误删）
                ThreadLocal<String> lockValueLocal = new ThreadLocal<>();
                lockValueLocal.set(lockValue);
                return true;
            }
            // 加锁失败，重试
            try {
                Thread.sleep(retryMs);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            }
            retryCount++;
        }
        return false;
    }

    // 释放分布式锁（用Lua脚本确保原子性）
    public void unlock(String lockKey) {
        ThreadLocal<String> lockValueLocal = new ThreadLocal<>();
        String lockValue = lockValueLocal.get();
        if (lockValue == null) {
            return;
        }
        // Lua脚本：仅当value匹配时才删除锁，避免误删其他线程的锁
        String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        DefaultRedisScript<Long> script = new DefaultRedisScript<>(luaScript, Long.class);
        redisTemplate.execute(script, Collections.singletonList(lockKey), lockValue);
        lockValueLocal.remove();
    }

    public boolean tryLock(String lockKey) {
        return tryLock(lockKey, DEFAULT_EXPIRE, DEFAULT_RETRY, DEFAULT_RETRY_TIMES);
    }
}
